import { Injectable, HttpService, forwardRef, Inject, HttpException, Logger } from '@nestjs/common';
import { ResTokenDto, WxNetWorkDto, WxUserInfoDto, WxWebAuthModel, WxQRTicketModel, WxJsApiTicketModel } from './model/common';
import { WXAPI, GetSearchParams, AppKey } from '../../common/define';
import { WxredisService } from '../../tools/wxredis/wxredis.service';
import { randomBytes, createHash } from 'crypto';

@Injectable({scope: 1})
export class WxhttpService {

    constructor(
        private readonly httpService: HttpService,
        private readonly wxRedisService: WxredisService,
    ) {}

    // 获取token对象
    async getWxToken(): Promise<ResTokenDto> {

        const params = {grant_type: 'client_credential', appid: AppKey.WX.appID, secret: AppKey.WX.appsecret};
        const query = GetSearchParams(params);
        const url = WXAPI.accessToken + query;

        const res = await this.httpService.get<ResTokenDto>(url).toPromise();    
        this.wxRedisService.set('token', res.data.access_token, 60 * 5);
        return res.data;
    }
    // 获取token string
    async getWxTokenString() {

        const token = await this.wxRedisService.get<string>('token');
        if (token) {
            return token;
        } else {
            const tokenObj = await this.getWxToken();
            return tokenObj.access_token;
        }
    }

    // network check
    async checkWxNetwork(): Promise<WxNetWorkDto> {

        const token = await this.wxRedisService.get('token');
        const url = WXAPI.checkNet + GetSearchParams({access_token: token});
        const paramsData = {action: 'all', check_operator: 'DEFAULT'};
        const res = await this.httpService.post<WxNetWorkDto>(url, paramsData).toPromise();
        return res.data;
    }

    // 获取用户信息
    async getWxUserInfoWithToken(token: string, openid: string): Promise<WxUserInfoDto> {

        const params = {access_token: token, openid};
        const url = WXAPI.getWxUserInfo + GetSearchParams(params);

        const res = await this.httpService.get<WxUserInfoDto>(url).toPromise();
        this.wxRedisService.set('wxUserInfoData', res.data);
        return res.data;
    }
    // 获取缓存数据
    async getWxUserInfoWithCache() {
        return this.wxRedisService.get<WxUserInfoDto>('wxUserInfoData');
    }

    // 处理authcode
    async handleAuthWxCode(authCode: string) {
        this.wxRedisService.set('wxAtuhCode', authCode);
    }

    // 获取wx用户授权回调
    async getWxAuthRedirect(auth: { code: string, state: string }) {
        Logger.log(JSON.stringify(auth),'WxAuthRedirectQuery')
        this.wxRedisService.set('wxAtuhCode', auth.code);
        const params = {appid: AppKey.WX.appID, secret: AppKey.WX.appsecret, code: auth.code, grant_type: 'authorization_code'};
        const url = WXAPI.getWebAuthToken + GetSearchParams(params);
        // 获取用户accesstoken
        const res = await this.httpService.get<WxWebAuthModel>(url).toPromise();
        this.wxRedisService.set('wxAtuhData', res.data);
        return res.data;
    }

    // 定时刷新webtoken
    async getRefreshToken() {

        const authData = await this.wxRedisService.get<WxWebAuthModel>('wxAtuhData');
        const obj = {appid: AppKey.WX.appID, grant_type: 'refresh_token', refresh_token: authData.refresh_token};
        const params = GetSearchParams(obj);
        const url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token' + GetSearchParams(params);
        // 刷新token
        const res = await this.httpService.get<WxWebAuthModel>(url).toPromise();
        this.wxRedisService.set('wxAtuhData', res.data);
        return res;
    }

    // 生成ticket
    async createQRTicket(id: number, isLimitTime?: boolean) {

        const token = await this.wxRedisService.get('token');
        if (!token) {
            this.getWxToken();
        }
        const url = WXAPI.qrcodeCreate + GetSearchParams({access_token: token});
        let params = null;
        if (isLimitTime) {
            params = {expire_seconds: 604800, action_name: 'QR_SCENE', action_info: {scene: {scene_id: id}}} ;
        } else {
            params = {action_name: 'QR_LIMIT_SCENE', action_info: {scene: {scene_id: id}}} ;
        }

        const res = await this.httpService.post<WxQRTicketModel>(url, params).toPromise();
        // console.log(token, '\nparams\n', params, 'data\n', res.data);
        if (res.status === 200) {
            return res.data;
        } else {
            throw new HttpException('获取ticket失败', 404);
        }

    }
    // 生成字符串类型
    async createStrQRTicket(scene: string, isLimitTime?: boolean) {

        const token = await this.wxRedisService.get('token');
        if (!token) {
            this.getWxToken();
        }
        const url = WXAPI.qrcodeCreate + GetSearchParams({access_token: token});
        let params = null;
        if (isLimitTime) {
            params = {expire_seconds: 604800, action_name: 'QR_SCENE', action_info: {scene: {scene_str: scene}}} ;
        } else {
            params = {action_name: 'QR_LIMIT_SCENE', action_info: {scene: {scene_str: scene}}} ;
        }
        const res = await this.httpService.post<WxQRTicketModel>(url, params).toPromise();
        return res.data;

    }

    // 获取二维码
    async getQRCode(id: number) {
        const ticketRes = await this.createQRTicket(id);
        // const ticketRes = await this.createStrQRTicket('text-car-no1');
        const ticket = ticketRes.ticket;

        const ticketEncode = encodeURI(ticket);
        const url = WXAPI.showQrcode + GetSearchParams({ticket: ticketEncode});

        // console.log(url);
        const res = await this.httpService.get<ArrayBuffer>(url, {responseType: 'arraybuffer'}).toPromise();
        const u8Arr = new Uint8Array(res.data);
        const buff = Buffer.from(u8Arr);
        const bs = buff.toString('base64');
        return bs;
    }

    // 获取js sha1签名
    async getJsApiSign(url: string) {
        const ticket = await this.getJsSdkTicket();
        const sign = this.createJsSignatureWithTicket(ticket.ticket, url);
        return sign;
    }

    // 通过获取userInfo
    async getUserInfoWithOpenID(openId: string) {
        // token
        const token = await this.getWxTokenString();
        const url = WXAPI.getUserInfoWithOpenId + GetSearchParams({access_token: token, openid: openId});
        const res = await this.httpService.get<WxUserInfoDto>(url).toPromise();

        return res.data;
    }

    // 生成js-sdk ticket
    async getJsSdkTicket() {
        // token
        const token = await this.getWxTokenString();
        const url = WXAPI.getTicket + GetSearchParams({access_token: token, type: 'jsapi'});
        const ticketRes = await this.httpService.get<WxJsApiTicketModel>(url).toPromise();
        // console.log('ticketRes', ticketRes.data);

        Logger.log(JSON.stringify(ticketRes.data), 'ticketRes');
        return ticketRes.data;
    }

    // 生成js-sdk 随机签名
    createJsSignatureWithTicket(ticket: string, urlStr: string) {

        // 生成6位随机
        const noncestr = randomBytes(16).toString('hex');
        const jsapi_ticket = ticket;
        const timestamp =  Math.round(Date.now() / 1000);
        const url =  urlStr;
        const appId =  AppKey.WX.appID;

        const ret = {jsapi_ticket, noncestr, timestamp, url};
        // const params = {noncestr};
        const rawString = GetSearchParams(ret).substring(1);

        console.log('rawString', rawString);

        const hash = createHash('sha1');
        const signature = hash.update(rawString).digest('hex');

        const params =  {noncestr, timestamp, signature, encodeUrl: url, appId};
        return params;
    }

}
